﻿using Core;
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading.Tasks;

namespace DDS.IoT.Modbus
{
    public class ModbusRtuService
    {

        private static IModbusMaster master;
        public static SerialPort port;
        public delegate void ReadInputTimeoutEventHandler(dynamic args, TimeoutException timeout);
        public static event ReadInputTimeoutEventHandler ReadInputTimeout;
        public static void TriggerReadInputTimeout()
        {
            if (ReadInputTimeout != null)
            {
                ReadInputTimeout(new { slaveAddress = 1, startAddress = 1, numberOfPoints = 1 }, new TimeoutException("超时测试"));
            }
        }
        public static bool IsOpen() {
            if (port == null)
                return false;
            return port.IsOpen;
        }

        public static void Close()
        {
            if (port != null && port.IsOpen)
                port.Close();
        }
        public static void Open()
        {
            if (port != null && !port.IsOpen)
                port.Open();
        }
        public static void Init(string portName, int baudRate = 9600,int readTimeout=3000)
        {
            Close();
            port = new SerialPort(portName, baudRate,Parity.None, 8, StopBits.One);
            port.DtrEnable = true;
            port.RtsEnable = true;
            master = ModbusSerialMaster.CreateRtu(port);
            master.Transport.ReadTimeout = readTimeout;
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "串口打开成功");
        }
        /// <summary>
        /// 01 读取单个线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        /// <returns></returns>
        public static bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints) {
            var buffers = master.ReadCoils(slaveAddress, startAddress, numberOfPoints);
            var datas = "";
            for (int i = 0; i < buffers.Length; i++)
            {
                datas += (buffers[i] + " ");
            }
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "读取单个线圈，从机地址：{0} 开始地址：{1} 范围：{2} 返回数据：{3}", slaveAddress, startAddress, numberOfPoints, datas);
            return buffers;
        }
        /// <summary>
        /// 02 读取输入线圈/离散量线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        /// <returns></returns>
        public static bool[] ReadInputs(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            var buffers= master.ReadInputs(slaveAddress, startAddress, numberOfPoints);
            var datas = "";
            for (int i = 0; i < buffers.Length; i++)
            {
                datas += (buffers[i] + " ");
            }
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "读取输入线圈/离散量线圈，从机地址：{0} 开始地址：{1} 范围：{2} 返回数据：{3}",slaveAddress,startAddress,numberOfPoints, datas);
            return buffers;
        }
        /// <summary>
        /// 04 读取输入寄存器
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        /// <returns></returns>
        public static ushort[] ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            ushort[] buffers = null;
            try
            {
                buffers = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);
                var datas = "";
                for (int i = 0; i < buffers.Length; i++)
                {
                    datas += (buffers[i] + " ");
                }
                LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "读取输入寄存器 地址：{0} 返回数据：{1}", slaveAddress, datas);
            }
            catch (Exception ex)
            {
                if (ex is System.TimeoutException)
                {
                    if (ReadInputTimeout != null)
                    {
                        ReadInputTimeout(new { slaveAddress= slaveAddress, startAddress= startAddress, numberOfPoints = numberOfPoints },ex as TimeoutException);
                    }
                }
                throw ex;
            }
            return buffers;
        }
        /// <summary>
        /// 03 读取保持寄存器
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        /// <returns></returns>
        public static ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
        {
            var buffers = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
            var datas = "";
            for (int i = 0; i < buffers.Length; i++)
            {
                datas+=(buffers[i] + " ");
            }
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "读取保持寄存器 地址：{0} 返回数据：{1}", slaveAddress, datas);

            return buffers;
        }
        /// <summary>
        /// 05 写单个线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numberOfPoints"></param>
        public static void WriteSingleCoil(byte slaveAddress, ushort startAddress, bool value)
        {
            master.WriteSingleCoil(slaveAddress, startAddress, value);
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "写单个线圈 地址：{0} 写入数据：{1}", slaveAddress, value);
        }

        /// <summary>
        /// 06写单个输入线圈/离散量线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        public static void WriteSingleRegister(byte slaveAddress, ushort startAddress, ushort value)
        {
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "写单个输入线圈/离散量线圈 地址：{0} 开始位置：{1} 写入数据：{2}", slaveAddress, startAddress, value);

            master.WriteSingleRegister(slaveAddress, startAddress, value);
        }
        /// <summary>
        /// 06写单个输入线圈/离散量线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        public static Task WriteSingleRegisterAsync(byte slaveAddress, ushort startAddress, ushort value)
        {
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "写单个输入线圈/离散量线圈（异步） 地址：{0} 开始位置：{1} 写入数据：{2}", slaveAddress, startAddress, value);
            return master.WriteSingleRegisterAsync(slaveAddress, startAddress, value);
        }
        /// <summary>
        /// 0F 写一组线圈
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        public static void WriteMultipleCoils(byte slaveAddress, ushort startAddress, bool[] value)
        {
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "写一组线圈 地址：{0} 开始位置：{1} 写入数据：{2}", slaveAddress, startAddress, value);

            master.WriteMultipleCoils(slaveAddress, startAddress, value);
        }
        /// <summary>
        /// 10 写一组保持寄存器
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="value"></param>
        public static void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] value)
        {
            LogProvider.Default.Log(Infrastructure.Logging.LogLevel.Info, null, "写一组保持寄存器 地址：{0} 开始位置：{1} 写入数据：{2}", slaveAddress, startAddress, value);
            master.WriteMultipleRegisters(slaveAddress, startAddress, value);
        }

    }
}
